/*********************************************************************
* 文件名称: wxcoder.c
* 说 明: 
* 作 者: lyfan
* 时 间: 2021.08.10
*********************************************************************/ 
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <dlfcn.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <time.h>
#include <openssl/des.h>
#include <stdarg.h>

#include "csdn_codereader.h"
#include "wxshell.h"
#include "Dongle_API.h"


extern char __idata_start[], __idata_end[];
extern unsigned long __idata_size;

static bool modstatus = false;
static bool donglestatus = false;
static void *handle = NULL;
static DONGLE_INFO *pKEYList = NULL;
static DONGLE_HANDLE hKey = NULL;
static pthread_mutex_t loadmutex = PTHREAD_MUTEX_INITIALIZER;

static unsigned char key_data[6][8] = {
	{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
	{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
	{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
	{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
	{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
	{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
};

static unsigned char cbc_key[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
static unsigned char cbc_iv[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};


//////////////////////////////////////////////////////////////////////////
// 算法初始化调用接口
static int (*CsdnCodeReader_init)(int proc_flag, int in_width, int in_height);

// 算法运行调用接口
static int (*CsdnCodeReader_process)(int proc_flag, unsigned char *im, int im_w, int im_h, char **results_ptr);

// 算法释放调用接口
static void (*CsdnCodeReader_release)(int proc_flag);

// 算法参数设置调用接口
static void (*Algorithm_codeReader_setParam)(int proc_flag, CodeReaderParamSet params);

// 算法参数读取（获取）调用接口
static void (*Algorithm_codeReader_getParam)(int proc_flag, CodeReaderParamSet *params);


static void StrPrintf(LPCTSTR fmt, ...)
{
	va_list vals;
	char buf[1024];

	memset(buf, 0, sizeof(buf));
	va_start(vals, fmt); 
	vsprintf(buf, fmt, vals);
	va_end(vals);
//	strcat(buf, "\n");
	printf(buf);

	return;
}

static DWORD showRet(const char *name , DWORD dwRet)
{
	if (DONGLE_SUCCESS != dwRet){
		StrPrintf("\r\n%s retcode=%08X\r\n",name, dwRet);
	} else{
		StrPrintf("\r\n%s success\r\n",name, dwRet);
	}

	return dwRet;
}

static void ShowBinHex(unsigned char* pBin, int len)
{
	// Show 16 bytes each line.
	int  i, j ,k, kk;
	int  lLines = len / 16;
	int  lCharInLastLine = 0;

	if(0 != len % 16){
		lCharInLastLine = len - lLines * 16;
	}

	for(i = 0; i < lLines; ++i){
		for(j = 0; j < 16; ++j){
			printf("%02X ", pBin[16 * i + j]);
			if(j == 7)
				printf("- ");
		}
		printf("    \n");
	}

	if(0 != lCharInLastLine){
		for(j = 0; j < lCharInLastLine; ++j){
			printf("%02X ", pBin[16 * i + j]);
			if(j == 7 && lCharInLastLine > 8)
				printf("- ");
		}

		k = 0;
		k += ((16 - lCharInLastLine) * 3);
		if(lCharInLastLine <= 8){
			k += 2;
		}

		for(kk = 0; kk < k; ++kk)
			printf(" ");
		printf("    \n");
	}
	printf("\n");
}

static void DongleEnum(int *pCount)
{
	int i;
	DWORD dwRet;

	*pCount = 0;
	dwRet = Dongle_Enum(NULL, pCount);
	if (dwRet != 0 || *pCount == 0){
		StrPrintf("ROCKEY-ARM not found, Dongle_Enum(1) = %08X\r\n", dwRet);
		return;
	}

	pKEYList = malloc(sizeof(DONGLE_INFO) * (*pCount));
	dwRet = Dongle_Enum(pKEYList, pCount);
	if (dwRet != 0){
		StrPrintf("ROCKEY-ARM not found, Dongle_Enum(2) = %08X\r\n", dwRet);
		free(pKEYList);
		return;
	}

	for (i = 0; i < *pCount; i++){
		StrPrintf("======KEY: %d======\r\n", i);
		StrPrintf("Version:%04X\r\n", pKEYList[i].m_Ver);
		StrPrintf("BirthDay: ");
		ShowBinHex(pKEYList[i].m_BirthDay, 8); 
		StrPrintf("Agent:  %08X\r\n", pKEYList[i].m_Agent);
		StrPrintf("PID:    %08X\r\n", pKEYList[i].m_PID);
		StrPrintf("UserID: %08X\r\n", pKEYList[i].m_UserID);
		StrPrintf("Mother: %08X\r\n", pKEYList[i].m_IsMother);
		StrPrintf("HID: ");
		ShowBinHex(pKEYList[i].m_HID, 8);
	}
	StrPrintf("The number of Rockey-ARM : %d \n", i);

	free(pKEYList);
}

static DWORD verify_usb_dongle()
{
	DWORD retcode;
	int RemainCount, nOffset = 4096;
	BYTE rbuf[16], pData[16] = {0, 0, 0, 0, 0, 0, 0, 0,
								0, 0, 0, 0, 0, 0, 0, 0};

	//retcode = Dongle_VerifyPIN(hKey, FLAG_ADMINPIN, CONST_ADMINPIN, &RemainCount);
	retcode = Dongle_VerifyPIN(hKey, FLAG_USERPIN, CONST_USERPIN, &RemainCount);
	if (DONGLE_SUCCESS != retcode){
		printf("\r\nDongle_VerifyPIN(FLAG_USERPIN) retcode=%08X, RemainCount=%d\r\n", retcode, RemainCount);
		return retcode;
	}
	retcode = Dongle_ReadData(hKey, nOffset, rbuf, sizeof(rbuf));
	if (DONGLE_SUCCESS != retcode){
		return retcode;
	}
	if (memcmp(rbuf, pData, 16) != 0){
		return DONGLE_ERROR_UNKNOWN;
	}

	return DONGLE_SUCCESS;
}

static void SF_LogHex(uint8_t *buf, int len)
{
	int i = 0;

	while(i < len){
		fprintf(stdout, "%02x ", buf[i]);
		if ((i+1) % 8 == 0){
			fprintf(stdout, "\n");
		}
		i++;
	}
	fprintf(stdout, "\n");

	return ;
}

static int ssl_des_ecb(const char *datain, char *dataout, int crypt)
{
	DES_key_schedule ks;
	DES_cblock in, out;

	DES_set_key_unchecked(&key_data[0], &ks);
	memcpy(in, datain, 8);
	memset(out, 0, 8);
	memset(dataout, 0, 8);
	if (crypt == DES_ENCRYPT)
		DES_ecb_encrypt(&in, &out, &ks, DES_ENCRYPT);
	else
		DES_ecb_encrypt(&in, &out, &ks, DES_DECRYPT);
	memcpy(dataout, out, 8);

	return 0;
}

static int ssl_des_ede_ecb(const char *datain, char *dataout, int crypt)
{
	DES_cblock in, out;
	DES_key_schedule ks, ks2, ks3;

	DES_set_key_unchecked(&key_data[0], &ks);
	DES_set_key_unchecked(&key_data[1], &ks2);
	DES_set_key_unchecked(&key_data[2], &ks3);
	memcpy(in, datain, 8);
	memset(out, 0, 8);
	memset(dataout, 0, 8);
	if (crypt == DES_ENCRYPT)
		DES_ecb3_encrypt(&in, &out, &ks, &ks2, &ks, DES_ENCRYPT);
	else
		DES_ecb3_encrypt(&in, &out, &ks, &ks2, &ks, DES_DECRYPT);
	memcpy(dataout, out, 8);

	return 0;
}

static int ssl_des_cbc(const char *datain, char *dataout, int crypt)
{
	unsigned char cbc_in[8];
	unsigned char cbc_out[8];
	DES_cblock iv3;
	DES_key_schedule ks;

	DES_set_key_checked(&cbc_key, &ks);
	memset(cbc_out, 0, sizeof(cbc_out));
	memset(cbc_in, 0, sizeof(cbc_in));
	memset(dataout, 0, 8);
	memcpy(cbc_in, datain, 8);
	memcpy(iv3, cbc_iv, sizeof(cbc_iv));
	if (crypt == DES_ENCRYPT)
		DES_ncbc_encrypt(cbc_in, cbc_out, 8, &ks, &iv3, DES_ENCRYPT);
	else
		DES_ncbc_encrypt(cbc_in, cbc_out, 8, &ks, &iv3, DES_DECRYPT);
	memcpy(dataout, cbc_out, 8);

	return 0;
}

static int sslcrypt_lib_entry(const char *filein, char *fileout, int roundlen, DES_CRYPT_TYPE ctype, int crypt)
{
	int i = 0;

	for (; i < roundlen; i += 8){
		switch (ctype){
			case DES_ECB:
				ssl_des_ecb(filein, fileout, crypt);
				break;
			case DES_ECB3:
				ssl_des_ede_ecb(filein, fileout, crypt);
				break;
			case DES_CBC:
				ssl_des_cbc(filein, fileout, crypt);
				break;
			default:
				break;
		}
		filein += 8;
		fileout += 8;
	}

	return 0;
}

// 算法模块载入
int Csdn_ModuleLoad_Entry()
{
	int ret, devfd, Count = 0;
	uint32_t filelen, roundlen, rlen, datalen;
	int32_t traced;
	wx_algo_info algoinfo;
	char *filein = NULL, *fileout = NULL;
	DES_CRYPT_TYPE ctype = DES_NONE;
	DWORD retcode;
	struct timespec timeout;

	clock_gettime(CLOCK_REALTIME, &timeout);
	timeout.tv_sec += 1;
	if (0 != pthread_mutex_timedlock(&loadmutex, &timeout))
		return WX_eGetLockErr;
	if (modstatus){
		pthread_mutex_unlock(&loadmutex);
		return WX_eSuccess;
	}

	donglestatus = false;
	DongleEnum(&Count);
	if (Count <= 0){
		pthread_mutex_unlock(&loadmutex);
		return WX_eDongleGetErr;
	}

	retcode = Dongle_Open(&hKey, 0);
	if(DONGLE_SUCCESS != retcode){
		pthread_mutex_unlock(&loadmutex);
		return WX_eDongleGetErr;
	}

	retcode = verify_usb_dongle();
	if (DONGLE_SUCCESS != retcode){
		printf("\r\nverify_usb_dongle err =%08X\r\n", retcode);
		ret = WX_eDongleVerifyPIN;
		goto devfd_fail;
	}

	donglestatus = true;
	devfd = open("/dev/wxcoder", O_RDONLY);
	if (devfd < 0){
		printf("open wxcoder failed\n");
		ret = WX_eOpendevErr;
		goto devfd_fail;
	}
	traced = 0;
	ret = ioctl(devfd, WX_TRACE_DECT, &traced);
	if (ret < 0 || traced){
		printf("traced: %d - %d!\n", ret, traced);
		ret = WX_eInternalErr;
		goto filein_fail;
	}

	filelen = __idata_size;
	roundlen = (filelen/8) * 8 + (filelen % 8 ? 8 : 0);
	filein = malloc(roundlen);
	if (filein == NULL){
		printf("malloc filein failed, size: %d\n", roundlen);
		ret = WX_eNoneMemory;
		goto filein_fail;
	}

	memset(filein, 0, roundlen);
	datalen = 0;
	while (datalen < filelen){
		rlen = 4096 <= (filelen - datalen) ? 4096 : (filelen - datalen);
		memcpy(filein + datalen, __idata_start + datalen, rlen);
		datalen += rlen;
	}

	if (memcmp(filein, CRYPTMAGIC, 4) != 0){
		printf("broken library!\n");
		ret = WX_eBrokenLib;
		goto magic_fail;
	}

	fileout = malloc(roundlen - sizeof(WX_CRYPT_HEAD));
	if (fileout == NULL){
		printf("malloc fileout failed, size: %lu\n", roundlen - sizeof(WX_CRYPT_HEAD));
		ret = WX_eNoneMemory;
		goto magic_fail;
	}

	memset(fileout, 0, roundlen - sizeof(WX_CRYPT_HEAD));
	switch (((WX_CRYPT_HEAD *)filein)->cryptype){
		case DES_ECB:
			sslcrypt_lib_entry(filein + sizeof(WX_CRYPT_HEAD), fileout, roundlen - sizeof(WX_CRYPT_HEAD), DES_ECB, DES_DECRYPT);
			break;
		case DES_ECB3:
			sslcrypt_lib_entry(filein + sizeof(WX_CRYPT_HEAD), fileout, roundlen - sizeof(WX_CRYPT_HEAD), DES_ECB3, DES_DECRYPT);
			break;
		case DES_CBC:
			sslcrypt_lib_entry(filein + sizeof(WX_CRYPT_HEAD), fileout, roundlen - sizeof(WX_CRYPT_HEAD), DES_CBC, DES_DECRYPT);
			break;
		default:
			printf("unknown type-%d\n", ctype);
			ret = WX_eBrokenLib;
			goto decrypt_fail;
			break;
	}

	algoinfo.algo_start = fileout;
	algoinfo.algo_len = ((WX_CRYPT_HEAD *)filein)->filelen;
	algoinfo.magic = 0x12345678;
	ret = ioctl(devfd, WX_ALGOMODULE_LOAD, &algoinfo);
	if (ret < 0){
		printf("ioctl WX_ALGOMODULE_LOAD failed: %s\n", strerror(errno));
		ret = WX_eInternalErr;
		goto decrypt_fail;
	}

	close(devfd);
	free(filein);
	free(fileout);
	if((handle = dlopen("/dev/wxcoder", RTLD_NOW)) == NULL) {
		printf("dlopen - %s\n", dlerror());
		ret = WX_eModulErr;
		goto devfd_fail;
	}

	CsdnCodeReader_init = dlsym(handle, "CsdnCodeReader_init");
	if (CsdnCodeReader_init == NULL){
		printf("dlsym CsdnCodeReader_init - %s\n", dlerror());
		dlclose(handle);
		ret = WX_eModulErr;
		goto devfd_fail;
	}
	CsdnCodeReader_release = dlsym(handle, "CsdnCodeReader_release");
	if (CsdnCodeReader_release == NULL){
		printf("dlsym CsdnCodeReader_release - %s\n", dlerror());
		dlclose(handle);
		ret = WX_eModulErr;
		goto devfd_fail;
	}
	Algorithm_codeReader_getParam = dlsym(handle, "Algorithm_codeReader_getParam");
	if (Algorithm_codeReader_getParam == NULL){
		printf("dlsym Algorithm_codeReader_getParam - %s\n", dlerror());
		dlclose(handle);
		ret = WX_eModulErr;
		goto devfd_fail;
	}
	Algorithm_codeReader_setParam = dlsym(handle, "Algorithm_codeReader_setParam");
	if (Algorithm_codeReader_setParam == NULL){
		printf("dlsym Algorithm_codeReader_setParam - %s\n", dlerror());
		dlclose(handle);
		ret = WX_eModulErr;
		goto devfd_fail;
	}
	CsdnCodeReader_process = dlsym(handle, "CsdnCodeReader_process");
	if (CsdnCodeReader_process == NULL){
		printf("dlsym CsdnCodeReader_process - %s\n", dlerror());
		dlclose(handle);
		ret = WX_eModulErr;
		goto devfd_fail;
	}

	devfd = open("/dev/wxcoder", O_RDONLY);
	if (devfd < 0){
		printf("open wxcoder failed...\n");
		dlclose(handle);
		ret = WX_eOpendevErr;
		goto devfd_fail;
	}
	ret = ioctl(devfd, WX_DYN_LOAD);
	if (ret < 0){
		printf("ioctl WX_DYN_LOAD failed: %s\n", strerror(errno));
		close(devfd);
		dlclose(handle);
		ret = WX_eInternalErr;
		goto devfd_fail;
	}
	modstatus = true;
	close(devfd);
	pthread_mutex_unlock(&loadmutex);

	return WX_eSuccess;

decrypt_fail:
	free(fileout);
magic_fail:
	free(filein);
filein_fail:
	close(devfd);
devfd_fail:
	retcode = Dongle_ResetState(hKey);
	showRet("Dongle_ResetState()" , retcode);
	retcode = Dongle_Close(hKey);
	showRet("Dongle_Close()" , retcode);
	hKey = NULL;
	handle = NULL;
	modstatus = false;
	donglestatus = false;
	pthread_mutex_unlock(&loadmutex);

	return ret;
}

// 算法模块卸载
int Csdn_ModuleUnload_Entry()
{
	DWORD retcode;
	struct timespec timeout;

	clock_gettime(CLOCK_REALTIME, &timeout);
	timeout.tv_sec += 1;
	if (0 != pthread_mutex_timedlock(&loadmutex, &timeout))
		return WX_eGetLockErr;

	if (false == modstatus){
		pthread_mutex_unlock(&loadmutex);
		return WX_eSuccess;
	}

	modstatus = false;
	donglestatus = false;
	CsdnCodeReader_init = NULL;
	CsdnCodeReader_release = NULL;
	Algorithm_codeReader_getParam = NULL;
	Algorithm_codeReader_setParam = NULL;
	CsdnCodeReader_process = NULL;
	dlclose(handle);
	retcode = Dongle_ResetState(hKey);
	showRet("Dongle_ResetState()", retcode);
	retcode = Dongle_Close(hKey);
	showRet("Dongle_Close()", retcode);
	hKey = NULL;
	handle = NULL;
	pthread_mutex_unlock(&loadmutex);

	return WX_eSuccess;
}

// 线程号proc_flag范围为1~32(最大可以创建32个进程并行运行)，默认使用1
// 算法初始化调用接口
int CsdnCR_Init_Entry(int proc_flag, int in_width, int in_height)
{
	DWORD retcode;

	if (modstatus == false){
		return WX_eModulErr;
	} else if (donglestatus == false){
		return WX_eDongleVerifyPIN;
	}

	retcode = verify_usb_dongle();
	if (DONGLE_SUCCESS != retcode){
		printf("\r\nverify_usb_dongle err =%08X\r\n", retcode);
		donglestatus = false;
		return WX_eDongleVerifyPIN;
	}
	if (CsdnCodeReader_init(proc_flag, in_width, in_height) < 0)
		return WX_eAlgorithmErr;

	return WX_eSuccess;
}

// 算法释放调用接口
int CsdnCR_Release_Entry(int proc_flag)
{
	DWORD retcode;

	if (modstatus == false){
		return WX_eModulErr;
	} else if (donglestatus == false){
		return WX_eDongleVerifyPIN;
	}

	retcode = verify_usb_dongle();
	if (DONGLE_SUCCESS != retcode){
		printf("\r\nverify_usb_dongle err =%08X\r\n", retcode);
		donglestatus = false;
		return WX_eDongleVerifyPIN;
	}
	CsdnCodeReader_release(proc_flag);

	return WX_eSuccess;
}

// 算法参数读取（获取）调用接口
int CsdnCR_GetParam_Entry(int proc_flag, CodeReaderParamSet *params)
{
	DWORD retcode;

	if (modstatus == false){
		return WX_eModulErr;
	} else if (donglestatus == false){
		return WX_eDongleVerifyPIN;
	}

	retcode = verify_usb_dongle();
	if (DONGLE_SUCCESS != retcode){
		printf("\r\nverify_usb_dongle err =%08X\r\n", retcode);
		donglestatus = false;
		return WX_eDongleVerifyPIN;
	}
	Algorithm_codeReader_getParam(proc_flag, params);

	return WX_eSuccess;
}

// 算法参数设置调用接口
int CsdnCR_SetParam_Entry(int proc_flag, CodeReaderParamSet params)
{
	DWORD retcode;

	if (modstatus == false){
		return WX_eModulErr;
	} else if (donglestatus == false){
		return WX_eDongleVerifyPIN;
	}

	retcode = verify_usb_dongle();
	if (DONGLE_SUCCESS != retcode){
		printf("\r\nverify_usb_dongle err =%08X\r\n", retcode);
		donglestatus = false;
		return WX_eDongleVerifyPIN;
	}
	Algorithm_codeReader_setParam(proc_flag, params);

	return WX_eSuccess;
}

// 算法运行调用接口
int CsdnCR_Process_Entry(int proc_flag, unsigned char *im, int im_w, int im_h, char **results_ptr)
{
	DWORD retcode;
	static int icount = 0;

	if (modstatus == false){
		return WX_eModulErr;
	} else if (donglestatus == false){
		return WX_eDongleVerifyPIN;
	}

	if (icount++ % 10 == 0){
		retcode = verify_usb_dongle();
		if (DONGLE_SUCCESS != retcode){
			printf("\r\nverify_usb_dongle err =%08X\r\n", retcode);
			donglestatus = false;
			return WX_eDongleVerifyPIN;
		}
	}
	return CsdnCodeReader_process(proc_flag, im, im_w, im_h, results_ptr);
}



